package yartiss.engine.simulation.scheduling.policies.dp;

import java.util.SortedSet;

import yartiss.engine.simulation.IEnergyProfile;
import yartiss.engine.simulation.ISchedulingPolicy;
import yartiss.engine.simulation.events.EventGenerator;
import yartiss.engine.simulation.scheduling.policies.AbstractMonoProcessorSchedulingPolicy;
import yartiss.engine.simulation.scheduling.policies.tools.DPSlackEnergyManager;
import yartiss.engine.simulation.scheduling.policies.tools.DPSlacktimeManager;
import yartiss.engine.simulation.system.ITask;
import yartiss.engine.simulation.system.ITaskSet;
import yartiss.engine.simulation.system.taskset.TaskSetFactory;

public class EDegSchedulingPolicy extends AbstractMonoProcessorSchedulingPolicy{

	private final DPSlackEnergyManager slackenergyManager;
	private final DPSlacktimeManager slackTimeManager;

	public EDegSchedulingPolicy(DPSlacktimeManager slackTimeManager, DPSlackEnergyManager slackenergyManager) {
		this.slackTimeManager = slackTimeManager;
		this.slackenergyManager = slackenergyManager;
	}

	@Override
	public String getPolicyName() {
		return "EDeg";
	}

	@Override
	public ITaskSet createTaskSet() {
		return TaskSetFactory.getNewInstance("deadline");
	}
	private long wakeup = -1;
	
	
	private boolean ifComputeSlackenergy(long t, long dj, ITaskSet taskset){
		long time = -4;
		if(t==time)System.out.println(t+"--"+dj+"toto ="+taskset.getActiveTasks(t).first().getDeadline());
		for (ITask task : taskset) {
			long nextActivation  = t,nextDeadline;
			if(t==time)System.out.println("--------- "+task.getDeadline());
			do{
				nextActivation = task.getNextActivation(nextActivation);
				nextDeadline = task.getNextAbsoluteDeadline(nextActivation);
				// nextActivation>t && nextDeadline<= dj
			}while( nextActivation < dj && nextDeadline > dj);
			if(nextActivation > t && nextDeadline <= dj){
				if(t==time)System.out.println("@@@@@  "+nextActivation+"==="+nextDeadline);
				return true;
			}
		}
		return false;
	}

	@Override
	public ITask chooseNextTask(ITaskSet taskSet, IEnergyProfile energyProfile,	long date, EventGenerator evGen) {
		
		if(wakeup>0 && date < wakeup)return null;
		wakeup = -1;
		long time = -1;
		SortedSet<ITask> activeTasks = taskSet.getActiveTasks(date);
		if(!activeTasks.isEmpty()){
			ITask first = activeTasks.first();
			long dj = activeTasks.first().getNextAbsoluteDeadline( date);
			if(ifComputeSlackenergy(date, dj, taskSet)){
				if(energyProfile.getCurrentEnergyLevel() > energyProfile.getMinEnergyLevel()){
					long se = slackenergyManager.getSlackEnergy(date, taskSet, energyProfile);
					if(date==time)System.out.println(date+" : -----task="+activeTasks.first().getDeadline()+"---------------- compute se = "+se);
					if( se >= 0){
						
						long hlcet = energyProfile.howLongCanExecute(first);
						if(hlcet>0){
							evGen.generateEvent("check_energy_state",0, activeTasks.first(), date + hlcet, null);
							return activeTasks.first();
						}
						return null;
					}
				}
			}

			long hlcet = energyProfile.howLongCanExecute(first);
			if (hlcet <= 0) {
				long st = slackTimeManager.getSlackTime(date, taskSet);
				if(energyProfile.getCurrentEnergyLevel() < energyProfile.getMaxEnergyLevel() && st >0){
					long diff = energyProfile.howMuchCanRecharge(st) + energyProfile.getCurrentEnergyLevel() - energyProfile.getMaxEnergyLevel();
					if (diff > 0){
						st -= diff/energyProfile.getPowerRate(); 
					}
					evGen.generateEvent("check_energy_state",0, activeTasks.first(), date + st, null);
					if(date==time)System.out.println(date+" :slack time = "+st);
					wakeup = date + st;
				}
				else {
					evGen.generateEvent("energy_failure",0, activeTasks.first(), date, null);
				}
				return null;
			}
			else {
				if (hlcet < activeTasks.first().getRemainingCost())
					evGen.generateEvent("check_energy_state",0, activeTasks.first(), date + hlcet, null);
				return activeTasks.first();
			}
		}
		return  null;
	}

	@Override
	public ISchedulingPolicy newInstance() {
		return new EDegSchedulingPolicy(new DPSlacktimeManager(), new DPSlackEnergyManager());
	}

	

}
